package com.google.code.struts2jruby;

import java.util.Map;

import org.jruby.Ruby;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.builtin.IRubyObject;

import com.opensymphony.xwork2.ActionEventListener;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.DefaultActionInvocation;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.UnknownHandler;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStackFactory;

/**
 * An action invocation that is capable if invoking JRuby actions.
 */
public class RubyDefaultActionInvocation extends DefaultActionInvocation {
	private JRubyObjectFactory jrubyObjectFactory;

	public RubyDefaultActionInvocation(JRubyObjectFactory jrubyObjectFactory,
			Map context, boolean b) throws Exception {
		super(context, b);
		this.jrubyObjectFactory = jrubyObjectFactory;
	}

	@Override
	protected void createAction(Map contextMap) {
		String className = proxy.getConfig().getClassName();
		if (className.startsWith("{") && className.endsWith("}")) {
			try {
				action = jrubyObjectFactory.buildAction(proxy.getActionName(),
						proxy.getNamespace(), proxy.getConfig(), contextMap);
			} catch (Exception e) {
				throw new XWorkException("Unable to instantiate jruby object",
						e);
			}
		} else {
			super.createAction(contextMap);
		}
	}

	protected String invokeAction(Object action, ActionConfig actionConfig)
			throws Exception {
		if (action instanceof IRubyObject) {
			return invokeRubyAction((IRubyObject) action);
		} else {
			return super.invokeAction(action, actionConfig);
		}
	}

	/**
	 * This method interfaces with the JRuby internal API.
	 * 
	 * @return The return status of the <tt>execute</tt> method.
	 * @throws Exception
	 */
	public String invokeRubyAction(IRubyObject rubyObject) throws Exception {
		String method = proxy.getMethod();
		if (method == null) {
			method = "execute";
		}
		Ruby runtime = rubyObject.getRuntime();
		IRubyObject rubyResult = rubyObject.callMethod(runtime
				.getCurrentContext(), method);
		return (String) JavaEmbedUtils.rubyToJava(runtime, rubyResult,
				java.lang.String.class);
	}

	@Inject
	public void setContainer(Container container) {
		super.setContainer(container);
	}

	@Inject
	public void setObjectFactory(ObjectFactory objectFactory) {
		super.setObjectFactory(objectFactory);
	}

	@Inject(required = false)
	public void setUnknownHandler(UnknownHandler unknownHandler) {
		super.setUnknownHandler(unknownHandler);
	}

	@Inject
	public void setValueStackFactory(ValueStackFactory valueStackFactory) {
		super.setValueStackFactory(valueStackFactory);
	}

	@Inject(required = false)
	public void setActionEventListener(ActionEventListener actionEventListener) {
		super.setActionEventListener(actionEventListener);
	}

	protected void setActionProxy(ActionProxy proxy) {
		this.proxy = proxy;
	}
}
